package com.itheima.leyou.service.impl;

import com.itheima.leyou.client.ItemFeignClient;
import com.itheima.leyou.common.exception.LyException;
import com.itheima.leyou.search.dto.SearchParamDTO;
import com.itheima.leyou.search.entity.Goods;
import com.itheima.leyou.search.repository.GoodsRepository;
import com.itheima.leyou.service.IndexService;
import com.itheima.leyou.service.SearchService;
import com.leyou.item.dto.SpuDTO;
import com.leyou.starter.elastic.entity.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Mono;
import static com.itheima.leyou.search.constant.SearchConstants.*;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class SearchServiceImpl implements SearchService {

    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private ItemFeignClient itemFeignClient;

    @Autowired
    private IndexService indexService;


    @Override
    public Mono<List<String>> getSuggestion(String key) {
        if(StringUtils.isEmpty(key)){
            throw  new LyException(400,"请求参数不能为空");
        }
      return   goodsRepository.suggestBySingleField(SUGGESTION_FIELD,key);
    }

    @Override
    public Mono<PageInfo<Goods>> generateSearch(SearchParamDTO searchParamDTO) {
        //构建查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.fetchSource(DEFAULT_SOURCE_FIELD, new String[0]);//默认查询的字段，id
        //查询条件一定要放进去
        searchSourceBuilder.query(getQueryBuilder(searchParamDTO));
        //分页查询
        searchSourceBuilder.from(searchParamDTO.getFrom());
        searchSourceBuilder.size(searchParamDTO.getSize());


        //高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field(DEFAULT_SEARCH_FIELD);
        highlightBuilder.preTags(DEFAULT_PRE_TAG);
        highlightBuilder.postTags(DEFAULT_POST_TAG);
        searchSourceBuilder.highlighter(highlightBuilder);

        //排序
        if (StringUtils.isNotBlank(searchParamDTO.getSortBy())) {
            searchSourceBuilder.sort(searchParamDTO.getSortBy(), searchParamDTO.getDesc() ? SortOrder.DESC: SortOrder.ASC);
        }
       return goodsRepository.queryBySourceBuilderForPageHighlight(searchSourceBuilder);
    }

    /**
     * 条件查询抽取
     * @param searchParamDTO
     * @return
     */
    private QueryBuilder getQueryBuilder(SearchParamDTO searchParamDTO) {
        if(StringUtils.isBlank(searchParamDTO.getKey())){
            // 搜索条件为null，返回异常
            throw new LyException(400, "搜索条件不能为空！");
        }
        //匹配查询
        String key = searchParamDTO.getKey();
        //条件过滤
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        //给布尔添加查询条件
        boolQueryBuilder.must(QueryBuilders
                .matchQuery(DEFAULT_SEARCH_FIELD, key)
                .operator(Operator.AND));
        //过滤条件
        if(!CollectionUtils.isEmpty(searchParamDTO.getFilters())){
            //有值的话过滤
            for (Map.Entry<String, String> stringStringEntry : searchParamDTO.getFilters().entrySet()) {
                String queryKey = stringStringEntry.getKey();
                String queryValue = stringStringEntry.getValue();
                if("品牌".equals(queryKey)){
                    boolQueryBuilder.filter(QueryBuilders.termQuery("brandId",queryValue));
                }else if("分类".equals(queryKey)){
                    boolQueryBuilder.filter(QueryBuilders.termQuery("categoryId",queryValue));
                }else{
                    BoolQueryBuilder subBool = new BoolQueryBuilder();
                    subBool.must(QueryBuilders.termQuery("specs.name", queryKey));
                    subBool.must(QueryBuilders.termQuery("specs.value", queryKey));
                    boolQueryBuilder.filter(QueryBuilders.nestedQuery("specs",subBool, ScoreMode.None));
                }
            }
        }
        return boolQueryBuilder;
    }

    @Override
    public Mono<Map<String, List<?>>> searchFilter(SearchParamDTO searchParamDTO) {

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //条件查询
        searchSourceBuilder.query(getQueryBuilder(searchParamDTO));

        //聚合查询，不需要查询结果
        searchSourceBuilder.size(0);

        //添加聚合条件
        searchSourceBuilder.aggregation(AggregationBuilders.terms("brandAgg").field("brandId").size(30));
        searchSourceBuilder.aggregation(AggregationBuilders.terms("categoryAgg").field("categoryId").size(30));

        //添加specs聚合条件
        searchSourceBuilder
                .aggregation(AggregationBuilders.nested("specsAgg","specs")
                        .subAggregation(AggregationBuilders.terms("nameAgg").field("specs.name").size(30)
                        .subAggregation(AggregationBuilders.terms("valueAgg").field("specs.value").size(30)))
        );




        //异步聚合处理
        Mono<Aggregations> aggregations = goodsRepository
                .aggregationBySourceBuilder(searchSourceBuilder);



        //类型转化
        return   aggregations.map(aggregation -> {
            Map<String,List<?>> resultMap = new LinkedHashMap<>();


            Terms categoryTerms = aggregation.get("categoryAgg");
            //遍历buckets,获取categoryIds
            List<Long> categoryIds = categoryTerms
                    .getBuckets()
                    .stream()
                    .map(bucket -> ((Terms.Bucket) bucket)
                            .getKeyAsNumber())
                    .map(Number::longValue).collect(Collectors.toList());

            //不为空，根据categoryIds进行查询category信息
            if(!CollectionUtils.isEmpty(categoryIds)){
                resultMap.put("分类",itemFeignClient.queryCategoryByIds(categoryIds));
            }



            //根据聚合名称获得聚合结果对象
            Terms brandTerms = aggregation.get("brandAgg");
            //遍历buckets,获取brandIds
            List<Long> brandIds = brandTerms.getBuckets()
                    .stream()
                    .map(bucket ->
                            //获取bucket里面的key
                            ((Terms.Bucket) bucket)
                                    .getKeyAsNumber())
                    .map(Number::longValue).collect(Collectors.toList());

            //不为空，根据brnadIds进行查询brand信息
            if(!CollectionUtils.isEmpty(brandIds)){
                resultMap.put("品牌",itemFeignClient.queryBrandById(brandIds));

            }





            //解析specs
            //根据聚合名称获取nested
            Nested specsAgg = aggregation.get("specsAgg");
            //根据nested聚合名称获取子聚合
            Terms nameAgg = specsAgg.getAggregations().get("nameAgg");

            //将name和value的值存到map集合中
           nameAgg.getBuckets().forEach(bucket->{
               String resultKey = bucket.getKeyAsString();
               Terms valueAgg = bucket.getAggregations().get("valueAgg");
               List<String> valueList = valueAgg.getBuckets().stream().map(valueBucket -> {
                   String resultValue = valueBucket.getKeyAsString();
                   return resultValue;
               }).collect(Collectors.toList());
               resultMap.put(resultKey,valueList);
           });





          return resultMap;
        });

    }

    /**
     * 商品上架，搜索新增
     * @param spuId
     */
    @Override
    public void saveGoodsById(Long spuId) {
        // 需要spu
        SpuDTO spu = itemFeignClient.querySpuById(spuId);
        if(spu==null){
            Long cid3 = spu.getCid3();

            Long brandId = spu.getBrandId();

        }
        // 构建goods
        Goods goods =indexService.buildGoods (spu);
        // 写入索引库
        goodsRepository.save(goods);

    }

    /**
     * 删除索引库中的商品
     * @param spuId
     */
    @Override
    public void deleteGoodsById(Long spuId) {

        goodsRepository.deleteById(spuId);
    }
}
